/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - initial API and implementation
 */
package de.ipk_gatersleben.bit.bi.edal.primary_data;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.util.Calendar;
import java.util.UUID;

import javax.mail.internet.InternetAddress;

import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataDirectory;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataDirectoryException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntityVersion;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataEntityVersionException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataFile;
import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.EnumDublinCoreElements;
import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.MetaData;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.PersistentIdentifier;
import de.ipk_gatersleben.bit.bi.edal.primary_data.reference.PublicationStatus;

/**
 * VeloCity template generator to create HTML output for
 * {@link PrimaryDataDirectory}s and {@link PrimaryDataFile}s.
 * 
 * @author arendd
 */
class VeloCityHtmlGenerator {

	private static final String STRING_VERSION = "version";
	private static final String STRING_NO_PUBLIC_REFERENCE_FOR_THIS_VERSION_SET = "No Public Reference for this version set!";
	private static final String STRING_UNABLE_TO_LOAD_VERSIONS_OF = "unable to load versions of ";
	private static final String STRING_PUBLIC_REFERENCE_AND_VERSION_NUMBER_ARE_NOT_COMPATIBLE = "PublicReference and version number are not compatible";
	private static final String STRING_UNABLE_TO_INITIALIZE_APPROVAL_SERVICE_PROVIDER = "unable to initialize ApprovalServiceProvider: ";
	private static final String STRING_ALLOBJECTS = "allobjects";
	private static final String STRING_REVIEWER_CODE = "reviewerCode";
	private static final String STRING_INTERNAL_ID = "internalId";
	private static final String STRING_IDENTIFIER_TYPE = "identifierType";
	private static final String STRING_DATE = "date";
	private static final String STRING_ENTITY = "entity";
	private static final String STRING_ALL_ELEMENTS = "allElements";
	private static final String STRING_UNABLE_TO_WRITE_HTML_OUTPUT = "unable to write HTML output";
	private static final String CODING_UTF_8 = "UTF-8";
	private static final String STRING_SERVER_URL = "serverURL";

	/**
	 * Default constructor to load all VeloCity properties.
	 */
	protected VeloCityHtmlGenerator() {

		Velocity.setProperty("resource.loader", "class");
		Velocity.setProperty("class.resource.loader.class",
				"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
		Velocity.setProperty("runtime.log.logsystem.class",
				"org.apache.velocity.runtime.log.NullLogSystem");
		Velocity.setProperty("input.encoding", "UTF-8");
		Velocity.setProperty("output.encoding", "UTF-8");
		Velocity.init();
	}

	/**
	 * Generate the HTML output for an eMail that the root user was changed.
	 * 
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param newAddress
	 *            the address of the new root user.
	 * @param oldAddress
	 *            the address of the old root user.
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateEmailForChangedRootUser(final URL serverURL,
			final InternetAddress newAddress, final InternetAddress oldAddress)
			throws EdalException {

		VelocityContext context = new VelocityContext();

		/* set the address of the new root user */
		context.put("newRoot", newAddress);

		/* set the address of the old root user */
		context.put("oldRoot", oldAddress);

		/* set the server URL */
		context.put(STRING_SERVER_URL, serverURL);

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/ChangedRootUserTemplate.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output for an eMail for the double opt-in of a root
	 * user.
	 * 
	 * 
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param address
	 *            the address of the root user.
	 * @param the
	 *            {@link UUID} to identify the root user.
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateEmailForDoubleOptIn(final URL serverURL,
			final InternetAddress address, final UUID uuid)
			throws EdalException {

		VelocityContext context = new VelocityContext();

		/** create the URL to confirm the eMail address */

		String url = serverURL.toString() + EdalJettyServer.EDAL_PATH_SEPARATOR
				+ EdalHttpFunctions.LOGIN.toString()
				+ EdalJettyServer.EDAL_PATH_SEPARATOR + uuid.toString()
				+ EdalJettyServer.EDAL_PATH_SEPARATOR + address.getAddress();

		/** set the URL to confirm the email address */
		context.put(STRING_SERVER_URL, url);
		context.put("server", serverURL);
		context.put("root", address);
		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/DoubleOptInTemplate.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataDirectory} for the
	 * landing page of the HTTP handler.
	 * 
	 * @param directory
	 *            the {@link PrimaryDataDirectory} to present on the landing
	 *            page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateHtmlForDirectory(
			final PrimaryDataDirectory directory, final URL serverURL)
			throws EdalException {

		VelocityContext context = new VelocityContext();
		/* set entity */
		context.put(STRING_ENTITY, directory);
		/* set version */
		context.put(STRING_VERSION, directory.getCurrentVersion());
		/* set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());
		/* set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		try {
			context.put(STRING_ALLOBJECTS, directory.listPrimaryDataEntities());
		} catch (PrimaryDataDirectoryException e) {
			throw new EdalException(
					"unable to load entity list of the directory", e);
		}

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/DirectoryTemplate.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataDirectory} for the
	 * landing page of the HTTP handler. Special function for the landing page
	 * for the reviewer, who should approve this object.
	 * 
	 * @param directory
	 *            the {@link PrimaryDataDirectory} to present on the landing
	 *            page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param reviewerCode
	 *            the reviewerCode to identify a reviewer.
	 * @param internalId
	 *            the internal ID of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @param identifierType
	 *            the {@link PersistentIdentifier} of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateHtmlForDirectoryForReviewer(
			PrimaryDataDirectory directory, long versionNumber,
			String internalId, PersistentIdentifier identifierType,
			URL serverURL, int reviewerCode) throws EdalException {

		Calendar date = null;

		try {
			date = DataManager.getImplProv().getApprovalServiceProvider()
					.newInstance().getPublicReferenceByInternalId(internalId)
					.getCreationDate();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new EdalException(
					STRING_UNABLE_TO_INITIALIZE_APPROVAL_SERVICE_PROVIDER
							+ e.getMessage(), e);
		} catch (EdalException e) {
			throw e;
		}

		/**
		 * try if the given PublicReference matches to the given version number,
		 * otherwise send error message
		 */
		try {
			if (!directory.getVersionByDate(date).equals(
					directory.getVersionByRevisionNumber(versionNumber))) {
				return generateHtmlForErrorMessage(
						EdalJettyHandler.NOT_FOUND_STATUS,
						EdalJettyHandler.NOT_FOUND,
						STRING_PUBLIC_REFERENCE_AND_VERSION_NUMBER_ARE_NOT_COMPATIBLE,
						serverURL);
			}
		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException(STRING_UNABLE_TO_LOAD_VERSIONS_OF
					+ directory + " :" + e.getMessage(), e);
		}

		VelocityContext context = new VelocityContext();

		/** set identifierType of the PublicReference */
		context.put(STRING_IDENTIFIER_TYPE, identifierType.toString());

		/** set internalId of the PublicReference */
		context.put(STRING_INTERNAL_ID, internalId);

		/** set date of the PublicReference */
		context.put(STRING_DATE, date);

		/** set entity */
		context.put(STRING_ENTITY, directory);

		/** set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());

		/** set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set reviewer code */
		context.put(STRING_REVIEWER_CODE, String.valueOf(reviewerCode));

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		context.put("logourl",
				VeloCityHtmlGenerator.class.getResource("edal_scaled.png"));

		try {
			context.put(STRING_ALLOBJECTS,
					directory.listPrimaryDataEntitiesByDate(date));
		} catch (PrimaryDataDirectoryException e) {
			throw new EdalException(
					"unable to load entity list of the directory", e);
		}

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/DirectoryTemplateForReviewer.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataDirectory} for the
	 * landing page of the HTTP handler. Special function for the landing page
	 * for a screenshot of a
	 * {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * 
	 * @param versionNumber
	 *            the number of the {@link PrimaryDataEntityVersion} of this
	 *            {@link PrimaryDataDirectory}.
	 * @param directory
	 *            the {@link PrimaryDataDirectory} to present on the landing
	 *            page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param internalId
	 *            the internal ID of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @param identifierType
	 *            the {@link PersistentIdentifier} of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create the HTML output.
	 */
	protected StringWriter generateHtmlForDirectoryOfSnapshot(
			PrimaryDataDirectory directory, long versionNumber,
			String internalId, PersistentIdentifier identifierType,
			final URL serverURL) throws EdalException {

		Calendar date = null;

		try {
			date = DataManager.getImplProv().getApprovalServiceProvider()
					.newInstance().getPublicReferenceByInternalId(internalId)
					.getCreationDate();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new EdalException(
					STRING_UNABLE_TO_INITIALIZE_APPROVAL_SERVICE_PROVIDER
							+ e.getMessage(), e);
		}

		/**
		 * try if the given PublicReference matches to the given version number,
		 * otherwise send error message
		 */
		try {
			if (!directory.getVersionByDate(date).equals(
					directory.getVersionByRevisionNumber(versionNumber))) {
				return generateHtmlForErrorMessage(
						EdalJettyHandler.NOT_FOUND_STATUS,
						EdalJettyHandler.NOT_FOUND,
						STRING_PUBLIC_REFERENCE_AND_VERSION_NUMBER_ARE_NOT_COMPATIBLE,
						serverURL);
			}
		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException(STRING_UNABLE_TO_LOAD_VERSIONS_OF
					+ directory + " :" + e.getMessage(), e);
		}

		Logger log = DataManager.getImplProv().getLogger();

		PrimaryDataDirectory currentDirectory = directory;

		PrimaryDataEntityVersion primaryDataEntityVersion = null;

		try {
			primaryDataEntityVersion = currentDirectory
					.getVersionByRevisionNumber(versionNumber);

			boolean foundPublicReference = false;
			try {
				if (primaryDataEntityVersion.getPublicReference(identifierType)
						.getPublicationStatus()
						.equals(PublicationStatus.ACCEPTED)) {

					currentDirectory
							.switchCurrentVersion(primaryDataEntityVersion);
				}

			} catch (PrimaryDataEntityVersionException e) {
				log.debug(currentDirectory + " has no " + identifierType);
				while (!foundPublicReference) {
					try {
						log.debug("try ParentDirectory '"
								+ currentDirectory.getParentDirectory() + "'");

						if (currentDirectory.getParentDirectory() == null) {
							return generateHtmlForErrorMessage(
									EdalJettyHandler.NOT_FOUND_STATUS,
									EdalJettyHandler.NOT_FOUND,
									STRING_NO_PUBLIC_REFERENCE_FOR_THIS_VERSION_SET,
									serverURL);
						}

						if (currentDirectory.getParentDirectory()
								.getVersionByDate(date)
								.getPublicReference(identifierType)
								.getPublicationStatus()
								.equals(PublicationStatus.ACCEPTED)) {

							if (primaryDataEntityVersion.getRevisionDate()
									.before(date)) {

								log.debug(currentDirectory.getParentDirectory()
										+ " has " + identifierType);

								foundPublicReference = true;

								currentDirectory = directory;
							} else {
								return generateHtmlForErrorMessage(
										EdalJettyHandler.NOT_FOUND_STATUS,
										EdalJettyHandler.NOT_FOUND,
										STRING_NO_PUBLIC_REFERENCE_FOR_THIS_VERSION_SET,
										serverURL);
							}
						}

					} catch (PrimaryDataEntityVersionException
							| PrimaryDataDirectoryException e1) {

						log.debug("ParentDirectory has no " + identifierType);

						foundPublicReference = false;
						try {
							currentDirectory = currentDirectory
									.getParentDirectory();
						} catch (PrimaryDataDirectoryException e2) {
							throw new EdalException(
									"unable to get parent directory: "
											+ e.getMessage(), e);
						}
					}
				}
			}

		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException("unable to get version by version number: "
					+ e.getMessage(), e);
		}

		VelocityContext context = new VelocityContext();

		/** set date of the PublicReference */
		context.put(STRING_DATE, date);

		/** set identifier type of the PublicReference */
		context.put(STRING_IDENTIFIER_TYPE, identifierType.toString());

		/** set internalId of the PublicReference */
		context.put(STRING_INTERNAL_ID, internalId);

		/** set entity */
		context.put(STRING_ENTITY, currentDirectory);

		/** set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());

		/** set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		try {
			context.put(STRING_ALLOBJECTS,
					currentDirectory.listPrimaryDataEntitiesByDate(date));
		} catch (PrimaryDataDirectoryException e) {
			throw new EdalException(
					"unable to load entity list of the directory", e);
		}

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/DirectoryTemplateForSnapshot.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate HTML output for an Error message of the HHTP handler.
	 * 
	 * @param responseCode
	 *            the error code of the response, e.g. 404.
	 * @param title
	 *            the title for the HTML page.
	 * @param message
	 *            the error message.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateHtmlForErrorMessage(int responseCode,
			String title, String message, URL serverURL) throws EdalException {

		VelocityContext context = new VelocityContext();

		/* set responseCode */
		context.put("responseCode", responseCode);
		/* set title */
		context.put("title", title);
		/* set message */
		context.put("message", message);
		/* set server */
		context.put("server", serverURL.getHost());
		/* set port */
		context.put("port", serverURL.getPort());

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/HtmlMessageTemplate.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataFile} for the landing
	 * page of the HTTP handler.
	 * 
	 * @param file
	 *            the {@link PrimaryDataFile} to present on the landing page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateHtmlForFile(final PrimaryDataFile file,
			final URL serverURL) throws EdalException {

		VelocityContext context = new VelocityContext();
		/* set entity name */
		context.put(STRING_ENTITY, file);
		/* set version */
		context.put(STRING_VERSION, file.getCurrentVersion());
		/* set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());
		/* set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/FileTemplate.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataFile} for the landing
	 * page of the HTTP handler. Special function for the landing page for the
	 * reviewer, who should approve this object.
	 * 
	 * @param file
	 *            the {@link PrimaryDataFile} to present on the landing page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param reviewerCode
	 *            the reviewerCode to identify a reviewer.
	 * @param internalId
	 *            the internal ID of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @param identifierType
	 *            the {@link PersistentIdentifier} of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create output.
	 */
	protected StringWriter generateHtmlForFileForReviewer(PrimaryDataFile file,
			long versionNumber, String internalId,
			PersistentIdentifier identifierType, URL serverURL, int reviewerCode)
			throws EdalException {

		Calendar date = null;

		try {
			date = DataManager.getImplProv().getApprovalServiceProvider()
					.newInstance().getPublicReferenceByInternalId(internalId)
					.getCreationDate();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new EdalException(
					STRING_UNABLE_TO_INITIALIZE_APPROVAL_SERVICE_PROVIDER
							+ e.getMessage(), e);
		} catch (EdalException e) {
			throw e;
		}

		/**
		 * try if the given PublicReference matches to the given version number,
		 * otherwise send error message
		 */
		try {
			if (!file.getVersionByDate(date).equals(
					file.getVersionByRevisionNumber(versionNumber))) {
				return generateHtmlForErrorMessage(
						EdalJettyHandler.NOT_FOUND_STATUS,
						EdalJettyHandler.NOT_FOUND,
						STRING_PUBLIC_REFERENCE_AND_VERSION_NUMBER_ARE_NOT_COMPATIBLE,
						serverURL);
			}
		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException(STRING_UNABLE_TO_LOAD_VERSIONS_OF + file
					+ " :" + e.getMessage(), e);
		}

		VelocityContext context = new VelocityContext();

		/** set identifierType of the PublicReference */
		context.put(STRING_IDENTIFIER_TYPE, identifierType.toString());

		/** set internalId of the PublicReference */
		context.put(STRING_INTERNAL_ID, internalId);

		/** set date of the PublicReference */
		context.put(STRING_DATE, date);

		/** set entity */
		context.put(STRING_ENTITY, file);

		/** set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());

		/** set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set reviewer code */
		context.put(STRING_REVIEWER_CODE, String.valueOf(reviewerCode));

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/FileTemplateForReviewer.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}

	/**
	 * Generate the HTML output of a {@link PrimaryDataFile} for the landing
	 * page of the HTTP handler. Special function for the landing page for a
	 * screenshot of a
	 * {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * 
	 * @param versionNumber
	 *            the number of the {@link PrimaryDataEntityVersion} of this
	 *            {@link PrimaryDataFile}.
	 * @param file
	 *            the {@link PrimaryDataFile} to present on the landing page.
	 * @param serverURL
	 *            the {@link URL} of the HTTP server.
	 * @param internalId
	 *            the internal ID of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @param identifierType
	 *            the {@link PersistentIdentifier} of the
	 *            {@link de.ipk_gatersleben.bit.bi.edal.primary_data.file.PublicReference}
	 * @return the HTML output in a {@link StringWriter}.
	 * @throws EdalException
	 *             if unable to create the HTML output.
	 */
	public StringWriter generateHtmlForFileOfSnapshot(PrimaryDataFile file,
			long versionNumber, PersistentIdentifier identifierType,
			URL serverURL, String internalId) throws EdalException {

		Calendar date = null;

		try {
			date = DataManager.getImplProv().getApprovalServiceProvider()
					.newInstance().getPublicReferenceByInternalId(internalId)
					.getCreationDate();
		} catch (InstantiationException | IllegalAccessException e) {
			throw new EdalException(
					STRING_UNABLE_TO_INITIALIZE_APPROVAL_SERVICE_PROVIDER
							+ e.getMessage(), e);
		}

		/**
		 * try if the given PublicReference matches to the given version number,
		 * otherwise send error message
		 */
		try {
			if (!file.getVersionByDate(date).equals(
					file.getVersionByRevisionNumber(versionNumber))) {
				return generateHtmlForErrorMessage(
						EdalJettyHandler.NOT_FOUND_STATUS,
						EdalJettyHandler.NOT_FOUND,
						STRING_PUBLIC_REFERENCE_AND_VERSION_NUMBER_ARE_NOT_COMPATIBLE,
						serverURL);
			}
		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException(STRING_UNABLE_TO_LOAD_VERSIONS_OF + file
					+ " :" + e.getMessage(), e);
		}

		Logger log = DataManager.getImplProv().getLogger();

		PrimaryDataFile currentFile = file;

		PrimaryDataEntityVersion primaryDataEntityVersion = null;

		try {
			primaryDataEntityVersion = currentFile
					.getVersionByRevisionNumber(versionNumber);

			boolean foundPublicReference = false;
			try {
				if (primaryDataEntityVersion.getPublicReference(identifierType)
						.getPublicationStatus()
						.equals(PublicationStatus.ACCEPTED)) {

					currentFile.switchCurrentVersion(primaryDataEntityVersion);
				}

			} catch (PrimaryDataEntityVersionException e) {
				log.debug(currentFile + " has no " + identifierType);

				PrimaryDataDirectory currentDirectory = null;
				try {
					currentDirectory = currentFile.getParentDirectory();
				} catch (PrimaryDataDirectoryException e3) {

				}

				while (!foundPublicReference) {
					try {
						log.debug("try ParentDirectory '" + currentDirectory
								+ "'");

						if (currentDirectory == null) {
							return generateHtmlForErrorMessage(
									EdalJettyHandler.NOT_FOUND_STATUS,
									EdalJettyHandler.NOT_FOUND,
									STRING_NO_PUBLIC_REFERENCE_FOR_THIS_VERSION_SET,
									serverURL);
						}

						if (currentDirectory.getVersionByDate(date)
								.getPublicReference(identifierType)
								.getPublicationStatus()
								.equals(PublicationStatus.ACCEPTED)) {

							if (primaryDataEntityVersion.getRevisionDate()
									.before(date)) {
								log.debug(currentDirectory + " has "
										+ identifierType);
								foundPublicReference = true;

								currentFile = file;
							} else {
								return generateHtmlForErrorMessage(
										EdalJettyHandler.NOT_FOUND_STATUS,
										EdalJettyHandler.NOT_FOUND,
										STRING_NO_PUBLIC_REFERENCE_FOR_THIS_VERSION_SET,
										serverURL);
							}
						}

					} catch (PrimaryDataEntityVersionException e1) {

						log.debug("ParentDirectory has no " + identifierType);

						foundPublicReference = false;
						try {

							if (currentDirectory.getParentDirectory() == null) {
								throw new EdalException(
										"root Directory arrived -> no reference found"
												+ e.getMessage(), e);
							} else {
								currentDirectory = currentDirectory
										.getParentDirectory();
							}
						} catch (PrimaryDataDirectoryException e2) {
							throw new EdalException(
									"unable to get parent directory: "
											+ e.getMessage(), e);
						}
					}
				}
			}

		} catch (PrimaryDataEntityVersionException e) {
			throw new EdalException(
					"unable to get version by revision number: "
							+ e.getMessage(), e);
		}

		VelocityContext context = new VelocityContext();

		/** set identifier type */
		context.put(STRING_IDENTIFIER_TYPE, identifierType.toString());

		/** set entity name */
		context.put(STRING_ENTITY, file);

		/** set date of the PublicReference */
		context.put(STRING_DATE, date);

		/** set meta data */
		context.put(STRING_ALL_ELEMENTS, MetaData.ELEMENT_TYPE_MAP.keySet());

		/** set server URL */
		context.put(STRING_SERVER_URL, serverURL.toString());

		/** set internalId of the PublicReference */
		context.put(STRING_INTERNAL_ID, internalId);

		/** set some enums **/
		context.put("description", EnumDublinCoreElements.DESCRIPTION);
		context.put("title", EnumDublinCoreElements.TITLE);
		context.put("creator", EnumDublinCoreElements.CREATOR);
		context.put("format", EnumDublinCoreElements.FORMAT);
		context.put("year", Calendar.YEAR);

		StringWriter output = new StringWriter();

		Velocity.mergeTemplate(
				"de/ipk_gatersleben/bit/bi/edal/primary_data/FileTemplateForSnapshot.xml",
				CODING_UTF_8, context, output);

		try {
			output.flush();
			output.close();
		} catch (final IOException e) {
			throw new EdalException(STRING_UNABLE_TO_WRITE_HTML_OUTPUT, e);
		}
		return output;
	}
}